<?php

namespace common\service;


use common\exception\LogicException;
use common\model\MediaModel;
use common\model\OauthClientModel;
use common\model\OauthCodeModel;
use common\model\OauthDeviceModel;
use common\model\OauthTokenModel;
use common\model\TenantModel;
use common\model\UserModel;
use support\ErrorCode;
use think\Exception;

class OauthService
{
    /**
     * @var CentrifugalService
     */
    private $centrifugalService;

    public function __construct()
    {
        $this->centrifugalService = CentrifugalService::getInstance();
    }

    /**
     * 通过设备码清空登录信息
     * @param $deviceUniqueCode
     * @param int $userId
     * @return int|mixed
     */
    public function clearTokenByDeviceCode($deviceUniqueCode, int $userId)
    {
        $device = model(OauthDeviceModel::class)->where(['user_id' => $userId, 'device_unique_code' => $deviceUniqueCode])->find();
        if ($device) {
            return model(OauthTokenModel::class)->where(['device_id' => $device['id']])->delete();
        }
        return 0;
    }

    /**
     * 验证应用密钥对
     * @param $appKey
     * @param $appSecret
     */
    protected function checkAppSecret($appKey, $appSecret)
    {
        $oauthClientModel = model(OauthClientModel::class);
        $count = $oauthClientModel->where(["app_key" => $appKey, "app_secret" => $appSecret])->count();

        if ($count <= 0) {
            throw_strack_exception('Illegal request.', ErrorCode::ILLEGAL_REQUEST);
        }
    }

    /**
     * 所有应用列表
     * @param $param
     * @return array
     */
    public function selectClient($param)
    {
        $options['fields'] = 'id,name,app_key,app_secret,created,active,home_uri,redirect_uri';
        if (array_key_exists('page', $param)) {
            $options['page'] = $param['page'];
        }

        $OauthClientModel = model(OauthClientModel::class);
        $clientList = $OauthClientModel->selectData($options);

        //头像
        $mediaModel = model(MediaModel::class);
        $clientMedia = $mediaModel->field('link_id,thumb')->where(['module' => 'oauth'])->select();
        $clientMediaMap = array_column($clientMedia, null, 'link_id');

        foreach ($clientList['rows'] as $k => $item) {
            $avatar = isset($clientMediaMap[$item['id']]['thumb']) ? $clientMediaMap[$item['id']]['thumb'] : '';
            $clientList['rows'][$k]['avatar'] = $avatar;
            $clientList['rows'][$k]['checked'] = false;
        }

        return $clientList;
    }

    /**
     * 添加用户
     * @param $data
     * @return bool|string
     * @throws Exception
     */
    public function addClient($data)
    {
        $oauthClientModel = model(OauthClientModel::class);
        $resData = $oauthClientModel->addItem($data);
        if (!$resData) {
            throw_strack_exception($oauthClientModel->getError(), ErrorCode::ADD_CLIENT_FAILURE);
        }
        return $resData;
    }

    /**
     * 添加客户端 (同时上传缩略图)
     * @param $param
     * @return bool|string
     * @throws Exception
     */
    public function addClientWithMedia($param)
    {
        $clientAddResData = $this->addClient($param['data']);

        // 添加媒体
        if (isset($param['media'])
            && !empty($param['media'])
            && !empty($param['media']['type'])
            && !empty($param['media']['media_server'])
            && !empty($param['media']['media_data'])
        ) {
            $mediaData = $param['media'];
            $mediaData['module'] = 'oauth';
            $mediaData['link_id'] = (string)$clientAddResData['id'];
            $mediaService = new MediaService();
            $mediaAddData = $mediaService->upload($mediaData);
            $clientAddResData['avatar'] = $mediaAddData[0]['thumb'];
        } else {
            $clientAddResData['avatar'] = '';
        }

        return $clientAddResData;
    }


    /**
     * 修改客户端
     * @param $param
     * @return array|bool|mixed
     * @throws \Exception
     */
    public function modifyClient($param)
    {
        $oauthClientModel = model(OauthClientModel::class);
        $clientId = $oauthClientModel->where($param['filter'])->getField("id");

        if ($clientId >= 0) {
            $param['data']['id'] = $clientId;
            $resData = $oauthClientModel->modifyItem($param['data']);

            if (!$resData) {
                if ($oauthClientModel->getErrorCode() === -411112) {
                    return ['id' => $clientId];
                } else {
                    throw new LogicException($oauthClientModel->getError(), $oauthClientModel->getErrorCode());
                }
            }
            return $resData;
        } else {
            throw_strack_exception('Failed to modify oauth client.', ErrorCode::MODIFY_OAUTH_CLIENT_FAILURE);
        }
    }

    /**
     * 修改客户端（同时修改缩略图）
     * @param $param
     * @return array|bool|mixed
     * @throws \Exception
     */
    public function modifyClientWithMedia($param)
    {

        $resData = $this->modifyClient($param);

        if (isset($param['media'])
            && !empty($param['media'])
            && !empty($param['media']['type'])
            && !empty($param['media']['media_server'])
            && !empty($param['media']['media_data'])
        ) {
            $mediaData = $param['media'];
            $mediaData['module'] = 'oauth';
            $mediaData['link_id'] = (string)$resData['id'];
            $mediaService = new MediaService();
            $mediaAddData = $mediaService->upload($mediaData);
            $resData['avatar'] = $mediaAddData[0]['thumb'];
        } else {
            $resData['avatar'] = '';
        }

        return $resData;
    }

    /**
     * 删除客户端
     * @param $param
     * @return array
     */
    public function deleteClient($param)
    {
        $oauthClientModel = model(OauthClientModel::class);

        $clientIdData = $oauthClientModel->field('id')->where($param['filter'])->select();

        if ($clientIdData > 0) {
            $resData = $oauthClientModel->deleteItem($param['filter']);

            if (!$resData) {
                // 删除失败错误码 008
                throw_strack_exception($oauthClientModel->getError(), ErrorCode::DELETE_CLIENT_FAILURE);
            } else {
                // 返回成功数据
                return ['ids' => array_column($clientIdData, "id")];
            }
        } else {
            throw_strack_exception('No data was deleted.', ErrorCode::NO_DATA_WAS_DELETED);
        }
    }


    /**
     * 切换授权客户端激活状态
     * @param $param
     * @return array
     */
    public function switchClientActive($param)
    {
        $oauthClientModel = model(OauthClientModel::class);

        //查询应用是否存在
        $oauthUser = $oauthClientModel->field('id,active')->where($param['filter'])->select();

        if (empty($oauthUser)) {
            throw_strack_exception('Data does not exist.', ErrorCode::DATA_NOT_EXIST);
        }

        $resData = [];
        foreach ($oauthUser as $item) {
            $active = 1;
            if ($item['active'] == 1) {
                $active = 0;
            }
            $oauthClientModel->where(['id' => $item['id']])->setField('active', $active);
            $resData[] = $item['id'];
        }

        return ["ids" => $resData];
    }

    /**
     * 写入二维码信息
     * @param $qrCodeId
     * @param $accessToken
     * @return array
     */
    public function writeQRCodeId($qrCodeId, $accessToken)
    {
        // 三分钟内有效
        S($qrCodeId, [
            'token' => md5($accessToken),
            'expire' => time() + 180
        ]);
        return [];
    }

    /**
     * 通过token生成auth code
     * @param $param
     * @return mixed
     * @throws Exception
     * @throws \Exception
     */
    public function getAuthorizeCodeByToken($param)
    {
        $oauthTokenModel = model(OauthTokenModel::class);
        // 1. 获取token所属当前用户
        $userTokenData = $oauthTokenModel->field('id,user_id')->where(["access_token" => md5($param['token'])])->find();
        if (empty($userTokenData)) {
            throw new LogicException('Token does not exist or is wrong.', ErrorCode::INVALID_TOKEN);
        }

        $oauthClientModel = model(OauthClientModel::class);

        // 3. 获取当前客户端配置
        $oauthClientData = $oauthClientModel->where(["app_key" => $param['app_key']])->find();
        if (empty($oauthClientData)) {
            throw new LogicException("missing oauth client data", ErrorCode::MISSING_OAUTH_CLIENT_DATA);
        }

        // 4. 生成授权码 authorize code, 跳转到回调地址
        $redirectUrL = !empty($param['redirect_uri']) ? $param['redirect_uri'] : $oauthClientData['redirect_uri'];
        $deviceCode = $param['device_unique_code'] ?? "";
        $authorizeCodeUpdateData = [
            'app_key' => $param['app_key'],
            'user_id' => $userTokenData['user_id'],
            'redirect_uri' => $redirectUrL,
            'type' => 'temp',
            'scope' => $oauthClientData['scope'],
            'device_unique_code' => $deviceCode,
            'code' => md5(create_uuid())
        ];

        $oauthCodeModel = model(OauthCodeModel::class);
        // 保存授权码
        $insertId = $oauthCodeModel->addItem($authorizeCodeUpdateData);
        if (!$insertId) {
            throw new LogicException("add oauth code failure" . $oauthCodeModel->getError(), ErrorCode::ADD_OAUTH_CODE_FAILURE);
        }
        return $insertId;
    }

    /**
     * 获取授权令牌
     * @param $param
     * @return bool|mixed
     * @throws \Throwable
     */
    public function getAuthorizeToken($param)
    {
        // 1.验证code是否正确
        $oauthCodeModel = model(OauthCodeModel::class);
        $clientCodeData = $oauthCodeModel->where(['code' => $param['code']])->find();
        if (empty($clientCodeData)) {
            throw new LogicException('Authorization code error.', ErrorCode::AUTHORIZATION_CODE_ERROR);
        }
        $oauthClientModel = model(OauthClientModel::class);
        // 2. 获取当前client数据
        $oauthClientData = $oauthClientModel->where(["app_key" => $param['app_key']])->find();

        // 3. 验证 app_key、app_secret
        if ($oauthClientData['app_key'] !== $param['app_key']
            || $oauthClientData['app_secret'] !== $param['app_secret']) {
            throw new LogicException('Authorization code does not match the current application.', ErrorCode::AUTHORIZATION_CODE_NOT_MATCH_THE_CURRENT_APP);
        }
        // 4. 给当前用户生成、或者更新授权令牌
        $userData = model(UserModel::class)->find($clientCodeData['user_id']);
        $userService = new UserService();

        return $userService->generateLoginTokenByUserData($userData, $param['device_unique_code'], $param['ip']);
    }


    /**
     * 同步注册信息
     * @param $param
     * @return bool|string
     * @throws Exception
     */
    public function syncRegister($param)
    {

        // 判断是否存在短信验证码
        $smsService = new SMSService();
        if (!empty($param['verify_code']) && !$smsService->verifySMSCode('register', $param['data']['phone'], $param['verify_code'])) {
            throw_strack_exception('Invalid SMS verification code.', ErrorCode::INVALID_SMS_VERIFICATION_CODE);
        };

        $userService = new UserService();
        $param['data']['active'] = 1;

        return $userService->addUserWithMedia($param, true);
    }

    /**
     * @param $param
     * @return array
     * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
     */
    public function sendSMS($param)
    {

        $smsService = new SMSService();
        $resData = [];
        switch ($param['type']) {
            case 'register':
                $resData = $smsService->sendRegisterSMS($param['phone'], $param['verify_code']);
                break;
            case 'login':
                $resData = $smsService->sendLoginSMS($param['phone'], $param['verify_code']);
                break;
            case 'forgot':
                $resData = $smsService->sendForgotSMS($param['phone'], $param['verify_code']);
                break;
        }

        return $resData;
    }

    /**
     * 远端验证登陆
     * @param $param
     * @return array
     * @throws \Throwable
     */
    public function remoteVerifyLogin($param)
    {
        $userService = new UserService();

        return $userService->remoteVerifyLoginByPassword(['login_name' => $param['data']['phone'], 'password' => $param['data']['password']]);

    }

    /**
     * 发送系统通知
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function sendSystemNoticeMessage($param)
    {
        $detailData = $param['detail_data'] ?? [];
        $notifyUserMark = $param['notify_user_mark'] ?? "producer_assistant";
        $messageService = new MessageService();
        return $messageService->sendSystemNoticeMessage($param['data'], $detailData, $notifyUserMark);
    }

    /**
     * 渲染消息模板
     * @param $param
     * @return array
     */
    public function renderMsgTemplate($param)
    {
        $messageService = new MessageService();
        return $messageService->renderMsgTemplate($param['template_id'], $param['param']);
    }

    /**
     * 创建聊天
     * @param $data
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function createChat($data, $param)
    {
        $chatService = new ChatService();
        return $chatService->createChatAndChatUsers($data['chat'], $data['chat_users'] ?? [], $data['owner'] ?? 0);
    }

    /**
     * 添加聊天用户
     * @param $data
     * @param $param
     * @return mixed
     * @throws Exception
     */
    public function inviteChatUser($data, $param)
    {
        $chatUserService = new ChatUserService();
        return $chatUserService->inviteUser($data);
    }

    /**
     * 删除聊天用户
     * @param $param
     * @return array
     */
    public function deleteChatUser($param)
    {
        $chatUserService = new ChatUserService();
        return $chatUserService->deleteWithOutAuth($param['param']['filter']);
    }

    /**
     * 聊天会话查询
     * @param $param
     * @return mixed
     */
    public function findChat($param)
    {
        $chatService = CommonService::instance("chat");
        return $chatService->find($param['param']);
    }

    /**
     * 删除聊天
     * @param $param
     * @return array|int
     * @throws Exception
     */
    public function deleteChat($param)
    {
        $chatService = new ChatService();
        return $chatService->deleteChat($param['param']);
    }

    /**
     * 发送消息到指定用户的通道
     * @param $param
     * @return array
     */
    public function sendDataToUserChannel($param)
    {
        $userIdList = model(UserModel::class)->where($param['param']['filter'])->field('id')->column('id');
        $res = [];
        foreach ($userIdList as $userId) {
            $res[] = $this->centrifugalService->sendDataToMineChannel($param['param']['data'], $userId);
        }
        return $res;
    }

    /**
     * 修改租户框架协议状态
     * @param $param
     * @return mixed
     */
    public function markTenantFrameworkAgreementStatus($param)
    {
        return model(TenantModel::class)->modifyItem($param['data']);
    }

}
